package monitor

import (
	"fmt"
	"log"
	"os"
	"time"
)

// HandleFunc is callback for result
type HandleFunc func(s *Sample)

var (
	monitorProcesses []string
	logger           *log.Logger = nil
	running          bool        = false
	sys              SysInfo
	chExit                        = make(chan bool)
	handles          []HandleFunc = nil
)

// SetLogger set logger
func SetLogger(log *log.Logger) {
	logger = log
}

// SetMonitorProcess accepts names of processes
func SetMonitorProcess(names []string) {
	monitorProcesses = names
}

// AddHandle add callback handles
func AddHandle(h HandleFunc) {
	handles = append(handles, h)
}

func handleDefault(s *Sample) {

	fmt.Printf(" == %s ==\n", time.Now().Format("20060102-150405"))

	fmt.Printf("[CPU] - %0.1f%% -- USER %.2f, SYS %.2f, NICE %.2f, IDLE %.2f, IOWAIT %.2f\n", s.GetCPULoad(0), s.GetCPUUser(0),
		s.GetCPUSys(0), s.GetCPUNice(0), s.GetCPUIdle(0), s.GetCPUIoWait(0))
	fmt.Printf("[MEM] - %0.1f%% -- TOTAL %dKB, FREE %dKB, CACHED %dKB, BUFFER %dKB\n", s.GetMemUsaged(), s.GetMemTotalSize(),
		s.GetMemFreeSize(), s.GetMemCacheSize(), s.GetMemBufferSize())

	pids := s.GetProcessPids()

	for _, p := range pids {
		fmt.Printf("[%s] - %0.1f%% %0.1f%% -- PID %d, VSZ %dKB, RSS %dKB\n", s.GetProcessName(p),
			s.GetProcessCPULoad(p), s.GetProcessMemUsaged(p),
			p, s.GetProcessVMemSize(p), s.GetProcessMemSize(p))
	}
}

func handle(s *Sample) {

	handleDefault(s)

	for _, h := range handles {
		h(s)
	}
}

func monitor(period int) {

	// set period
	if period < 1 {
		period = 3
	}

	sys = &LinuxInfo{}
	sys.Init()

	s := NewSample(sys, true, true, monitorProcesses)

	go func() {

		for {

			delay := time.Duration(period) * time.Second

			t, err := s.Action()
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("Sample Time:", t)
			}

			handle(&s)

			if delay > t {
				time.Sleep(delay - t)
			}
		}
	}()

	running = true

	logger.Printf("Monitor started!")
	<-chExit

	//ticker.Stop()
	running = false
	logger.Printf("Monitor stopped!")
}

// Start is action for monitor begin
func Start(period int) error {

	if logger == nil {
		logger = log.New(os.Stdout, "psmon", log.Ldate|log.Ltime|log.Lshortfile)
	}

	if running {
		return fmt.Errorf("monitor is already running")
	}

	if len(monitorProcesses) > 0 {
		logger.Printf("Monitor processes:%v", monitorProcesses)
	} else {
		logger.Printf("Monitor CPU and MEM Only!")
	}

	if period < 1 {
		logger.Printf("Invalid polling interval:%d, set to default:3", period)
		period = 3
	}

	go monitor(period)

	return nil
}

// Stop is action to stop the monitor
func Stop() {
	chExit <- true
}
